package java8.myStream;

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

public class MyStreamTest {


    /**
     * 测试 规约 加和计算
     *
     * @author ZhangYi
     */
    @Test
    public void testReduce(){
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        // 求乘积
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2
        Integer max2 = list.stream().reduce(1, Integer::max);

        System.out.println("list求和：" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求积：" + product.get());
        System.out.println("list求和：" + max.get() + "," + max2);
    }

    /**
     * 测试redue2 sum,max,min,avg,count
     *
     * @author ZhangYi
     */
    @Test
    public void testReduce2(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));


        // 求总数
        Long count = personList.stream().collect(Collectors.counting());
        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        // 求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));


        System.out.println("员工总数：" + count);
        System.out.println("员工平均工资：" + average);
        System.out.println("员工工资总和：" + sum);
        System.out.println("员工工资所有统计：" + collect);
    }

    @Test
    public void testGroupBy(){
        //1. 多级分组
        //在这个例子中，员工首先按照部门(department)分组，然后在每个部门内再按年龄(age)分组。
        List<Employee> employees = new ArrayList<>();
        Map<String, Map<Integer, List<Employee>>> result = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.groupingBy(Employee::getAge)));

        //2. 分组后聚合计算
        // 展示了如何按部门分组后计算每个部门的平均薪资。
        Map<String, Double> avgSalaryByDepartment = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.averagingDouble(Employee::getSalary)));

        //3. 过滤后再分组
        // 我们只对全职员工按部门进行了分组
        Map<String, List<Employee>> fullTimeEmployeesByDepartment = employees.stream()
                .filter(employee -> employee.getStatus().equals("Full Time"))
                .collect(Collectors.groupingBy(Employee::getDepartment));

        //4. 自定义分类函数
        //在这个例子中，我们根据员工薪资自定义了一个分组逻辑，将员工分为“高薪”和“低薪”两组。
        Map<String, List<Employee>> groupByCustomCriteria = employees.stream()
                .collect(Collectors.groupingBy(employee -> {
                    if (employee.getSalary() > 50000) {
                        return "High Salary";
                    } else {
                        return "Low Salary";
                    }
                }));
        //5. 分组与平展映射
        Map<String, Integer> totalSalaryPerDepartment = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.reducing(0, Employee::getSalary, Integer::sum)));
    }


    public void testPartitioningBy(){
        //1. 多级分区
        //虽然partitioningBy直接支持一级分区，但可以通过嵌套使用来实现多级分区。不过，这通常会导致结果结构变得复杂，不如直接使用多次分组或自定义收集器直观。
        List<Employee> employees = new ArrayList<>();
        Map<Boolean, Map<Boolean, List<Employee>>> multiLevelPartition = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 50000,
                                                   Collectors.partitioningBy(e -> e.getAge() > 30)));
        //2. 分区后聚合操作
        Map<Boolean, Long> countByFullTime = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getStatus().equals("Full Time"),
                                                   Collectors.counting()));

    //    3. 自定义谓词逻辑
    //    这个例子展示了如何根据部门为IT且工作经验超过5年的自定义条件进行分区。
        Map<Boolean, List<Employee>> partitionByCustomLogic = employees.stream()
                .collect(Collectors.partitioningBy(e -> {
                    // 自定义逻辑，例如基于多个属性的复合判断
                    return e.getDepartment().equals("IT") && e.getPerformanceScore() > 5;
                }));

        // 4. 分区后再映射
        Map<Boolean, List<String>> namesBySalaryRange = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 70000,
                                                   Collectors.mapping(Employee::getName, Collectors.toList())));

        //分区与流的后续操作
        //这段代码首先按绩效评分分区，然后找到评分≥90的高绩效员工列表。
        //Map<Boolean, List<Employee>> highPerformers = employees.stream()
        //        .collect(Collectors.partitioningBy(e -> e.getPerformanceScore() >= 90))
        //        .entrySet().stream()
        //        .filter(entry -> entry.getKey())
        //        .findFirst()
        //        .orElseThrow(() -> new IllegalStateException("No high performers found"))
        //        .getValue();

    }
}
